/**
 * @file   PacketParser.cpp
 * @author Djuro Drljaca (djurodrljaca@gmail.com)
 * @date   2013-03-10
 * @brief  DataBus Packet Parser.
 *
 * Copyright (C) 2013  Djuro Drljaca <djurodrljaca@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <DataBus/Common/PacketParser.h>

DataBus::PacketParser::PacketParser()
    : m_packetList(),
      m_packetHeader(),
      m_packetPayload(),
      m_buffer(),
      m_state(State_WaitForStartOfFrame)
{
}

void DataBus::PacketParser::initialize()
{
    m_packetList.clear();
    m_buffer.clear();
    m_state = State_WaitForStartOfFrame;
}

bool DataBus::PacketParser::isPacketAvailable() const
{
    if (m_packetList.isEmpty())
    {
        return false;
    }
    else
    {
        return true;
    }
}

DataBus::Packet DataBus::PacketParser::takePacket()
{
    // Check if packet is available
    if (isPacketAvailable() == false)
    {
        // Error, packet is not available
        return Packet();
    }

    // Start reading next packet
    m_state = State_WaitForStartOfFrame;

    // Return packet
    return m_packetList.takeFirst();
}

void DataBus::PacketParser::processReceivedData(const QByteArray &data)
{
    // Add data to buffer
    m_buffer.append(data);

    // Execute state machine
    bool finished = false;

    while (finished == false)
    {
        switch (m_state)
        {
            case State_WaitForStartOfFrame:
            {
                finished = waitForStartOfFrame();
                break;
            }

            case State_ReadPacketHeader:
            {
                finished = readPacketHeader();
                break;
            }

            case State_ReadPacketPayload:
            {
                finished = readPacketPayload();
                break;
            }

            case State_WaitForEndOfFrame:
            {
                finished = waitForEndOfFrame();
                break;
            }

            default:
            {
                // Error, reinitialize the state machine
                m_state = State_WaitForStartOfFrame;
                finished = false;
                break;
            }
        }
    }
}

bool DataBus::PacketParser::waitForStartOfFrame()
{
    // Check if buffer is empty
    if (m_buffer.isEmpty())
    {
        // Wait for more data
        return true;
    }

    // Find start of frame (STX character)
    int index = m_buffer.indexOf('\x02');

    if (index < 0)
    {
        // STX was not found, clear complete buffer and wait for more data
        m_buffer.clear();
        return true;
    }

    // Remove garbage bytes before STX and the STX character
    m_buffer.remove(0, index + 1);

    // Start reading packet header
    m_state = State_ReadPacketHeader;

    if (m_buffer.size() < 6)
    {
        // Wait for more data
        return true;
    }
    else
    {
        // Process received data imediately
        return false;
    }
}

bool DataBus::PacketParser::readPacketHeader()
{
    // Check if buffer is full enough for a complete packet header
    if (m_buffer.size() < 6)
    {
        // Wait for more data
        return true;
    }

    // Try to parse the data
    if (PacketHeader::parse(m_buffer, 0, &m_packetHeader) == false)
    {
        // Error, invalid data for a packet header, wait for another start of frame
        m_state = State_WaitForStartOfFrame;

        return false;
    }

    // Check if packet payload is expected
    if (m_packetHeader.getPayloadSize() > 0)
    {
        // Start reading packet payload
        m_state = State_ReadPacketPayload;

        int size = 6 + m_packetHeader.getPayloadSize() + 1; // Header + Payload Data + Payload Checksum

        if (m_buffer.size() < size)
        {
            // Wait for more data
            return true;
        }
        else
        {
            // Process received data imediately
            return false;
        }
    }
    else
    {
        // Clear payload
        m_packetPayload.setData(QByteArray());

        // Wait for end of frame
        m_state = State_WaitForEndOfFrame;

        int size = 6 + 1; // Header + EOF

        if (m_buffer.size() < size)
        {
            // Wait for more data
            return true;
        }
        else
        {
            // Process received data imediately
            return false;
        }
    }
}

bool DataBus::PacketParser::readPacketPayload()
{
    // Check if buffer is full enough for a complete packet payload (data + checksum)
    int payloadSize = m_packetHeader.getPayloadSize();

    int size = 6 + payloadSize + 1; // Header + Payload Data + Payload Checksum

    if (m_buffer.size() < size)
    {
        // Wait for more data
        return true;
    }

    // Try to parse the data
    if (PacketPayload::parse(m_buffer, 6, payloadSize, &m_packetPayload) == false)
    {
        // Error, invalid data for a packet header, wait for another start of frame
        m_state = State_WaitForStartOfFrame;

        return false;
    }

    // Wait for end of frame
    m_state = State_WaitForEndOfFrame;

    if (m_buffer.size() < (8 + payloadSize))
    {
        // Wait for more data
        return true;
    }
    else
    {
        // Process received data imediately
        return false;
    }
}

bool DataBus::PacketParser::waitForEndOfFrame()
{
    // Check if buffer is full enough for a complete packet frame
    int payloadSize = m_packetHeader.getPayloadSize();

    int size = 6 + 1; // Header + EOF

    if (payloadSize > 0)
    {
        size += payloadSize + 1; // Header + Payload Data + Payload Checksm + EOF
    }

    if (m_buffer.size() < size)
    {
        // Wait for more data
        return true;
    }

    // Check if end of frame is valid (ETX character)
    if (m_buffer.at(size - 1) != '\x03')
    {
        // Error, invalid end of frame, reinitialize state machine and
        // imediately start processing received data
        m_state = State_WaitForStartOfFrame;

        return false;
    }

    // Remove packet data from the buffer
    m_buffer.remove(0, size);

    // Payload was successfully received, add it to packet list
    m_packetList.append(DataBus::Packet(m_packetHeader, m_packetPayload));

    // Wait for next packet
    m_state = State_WaitForStartOfFrame;

    if (m_buffer.isEmpty())
    {
        // Wait for more data
        return true;
    }
    else
    {
        // Process received data imediately
        return false;
    }
}
